/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.service.impl;

import es.silenus.detecta.dao.OrganizationDAO;
import es.silenus.detecta.dao.Page;
import es.silenus.detecta.dao.UserDAO;

import es.silenus.detecta.model.Organization;
import es.silenus.detecta.model.User;

import es.silenus.detecta.service.OrganizationService;

import org.springframework.security.GrantedAuthority;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;


/**
 * Default organization service, used when DETECTA is a standalone application.
 *
 * @author <a href="mailto:malonso@silenus-consultoria.es">Mariano Alonso</a>
 *
 * @since 24-jun-2009 12:59:20
 */
public class DefaultOrganizationService implements OrganizationService {
	private static final GrantedAuthority[] NO_AUTHORITIES = new GrantedAuthority[0];

	/**
	 * The organization DAO.
	 */
	private OrganizationDAO organizationDAO;

	/**
	 * The user DAO.
	 */
	private UserDAO userDAO;

	/**
	 * Sets the organization DAO.
	 *
	 * @param organizationDAO the organization DAO.
	 */
	public void setOrganizationDAO(final OrganizationDAO organizationDAO) {
		this.organizationDAO = organizationDAO;
	}

	/**
	 * Sets the user DAO.
	 *
	 * @param userDAO the user DAO.
	 */
	public void setUserDAO(final UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	/**
	 * Finds an organization by its identifier.
	 *
	 * @param id the organization id.
	 *
	 * @return the organization.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Organization findOrganizationById(Integer id) {
		return organizationDAO.findById(id);
	}

	/**
	 * Finds every organization.
	 *
	 * @return the organization list.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Organization> findAllOrganizations() {
		return organizationDAO.findAll();
	}

	/**
	 * Finds organizations by its name.
	 *
	 * @param name the name.
	 *
	 * @return the organization.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Organization findOrganizationByName(final String name) {
		return organizationDAO.findByName(name);
	}

	/**
	 * Finds organizations by example.
	 *
	 * @param example the example criteria.
	 *
	 * @return the list of organizations.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Organization> findOrganizationsByExample(Map<String, Object> example) {
		return organizationDAO.findByExample(example);
	}

	/**
	 * Finds organizations by example.
	 *
	 * @param sample the example criteria.
	 * @param start the start.
	 * @param limit the limit.
	 * @param sortFields the sort fields.
	 * @param sortDirections the sort directions.
	 *
	 * @return the page of results.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Page<Organization> findOrganizationsByExample(
		Map<String, Object> sample, int start, int limit, String[] sortFields, String[] sortDirections) {
		return organizationDAO.findByExample(sample, start, limit, sortFields, sortDirections);
	}

	/**
	 * Creates a new organization.
	 *
	 * @param organization the organization.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void createOrganization(Organization organization) {
		organizationDAO.save(organization);
	}

	/**
	 * Updates an existing organization.
	 *
	 * @param organization the organization.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void updateOrganization(Organization organization) {
		organizationDAO.update(organization);
	}

	/**
	 * Returns the names of all groups that this group manager controls.
	 *
	 * @return the array of group names.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public String[] findAllGroups() {
		List<String> names = organizationDAO.findNames();

		return names.toArray(new String[names.size()]);
	}

	/**
	 * Locates the users who are members of a group
	 *
	 * @param groupName the group whose members are required
	 *
	 * @return the usernames of the group members
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public String[] findUsersInGroup(String groupName) {
		List<String> names = organizationDAO.findUserLogins(groupName);

		return names.toArray(new String[names.size()]);
	}

	/**
	 * Creates a new group with the specified list of authorities.
	 *
	 * @param groupName the name for the new group
	 * @param authorities the authorities which are to be allocated to this group.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void createGroup(String groupName, GrantedAuthority[] authorities) {
		Organization organization = organizationDAO.findByName(groupName);

		if(organization == null) {
			organization = new Organization();
			organization.setName(groupName);
			organizationDAO.save(organization);
		}
	}

	/**
	 * Removes a group, including all members and authorities.
	 *
	 * @param groupName the group to remove.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void deleteGroup(String groupName) {
		Organization organization = organizationDAO.findByName(groupName);

		if(organization != null) {
			organizationDAO.remove(organization);
		}
	}

	/**
	 * Changes the name of a group without altering the assigned authorities or members.
	 *
	 * @param oldName the old name.
	 * @param newName the new name.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void renameGroup(String oldName, String newName) {
		Organization organization = organizationDAO.findByName(oldName);

		if(organizationDAO.findByName(newName) == null) {
			organization.setName(newName);
			organizationDAO.save(organization);
		}
	}

	/**
	 * Makes a user a member of a particular group.
	 *
	 * @param username the user to be given membership.
	 * @param group the name of the group to which the user will be added.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void addUserToGroup(String username, String group) {
		User user = userDAO.findByLogin(username);

		if(user != null) {
			Organization organization = organizationDAO.findByName(group);

			if(organization != null) {
				user.setOrganization(organization);
				userDAO.update(user);
			}
		}
	}

	/**
	 * Deletes a user's membership of a group.
	 *
	 * @param username the user
	 * @param groupName the group to remove them from
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void removeUserFromGroup(String username, String groupName) {
		// Do nothing, a user must belong to one organization
	}

	/**
	 * Obtains the list of authorities which are assigned to a group.
	 *
	 * @param groupName the group name.
	 *
	 * @return the array of granted authorities.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public GrantedAuthority[] findGroupAuthorities(String groupName) {
		return NO_AUTHORITIES;
	}

	/**
	 * Assigns a new authority to a group.
	 *
	 * @param groupName the group name.
	 * @param authority the authority.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void addGroupAuthority(String groupName, GrantedAuthority authority) {
		// Do nothing: groups - organizations do not have any authority
	}

	/**
	 * Deletes an authority from those assigned to a group
	 *
	 * @param groupName the group name.
	 * @param authority the authority.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
		// Do nothing: groups - organizations do not have any authority
	}

	/**
	 * Retrieves the alfresco URL.
	 *
	 * @param organizationName the organization name (which also is the folder name).
	 *
	 * @return the URL.
	 */
	public String getAlfrescoURL(final String organizationName) {
		// Not implemented
		return "";
	}
}
